package DataName;
use warnings;
use strict;
use FieldHash;
use SimpleError;

sub new {
    my $class = shift;
    bless +{ @_ }, $class;
}

sub set_data {
    my $self = shift;
    my ($data) = @_;
    error("no data provided") unless defined $data;
    $self->{Data} = $data;
}

sub get_data {
    my $self = shift;
    return $self->{Data};
}

sub guess_names {
    my $self = shift;
    my $log_struct = $self->{Data}{Log_Struct};
    tie my %fields, 'FieldHash', $log_struct->[0];
    ## for the log structure field names,
    ## please see file 'LogParse.pm' (constant 'LS_FIELD_NAMES')
    for my $i (1 .. $#$log_struct) {
        (tied %fields)->data($log_struct->[$i]);
        my $comment  = $fields{comment};
        my $pin_name = $fields{pin_name};
        my $result = $comment eq '-' ? '' : $comment;
        if ($result ne '') {
            $result =~ s/-$/low/;
            $result =~ s/\+$/high/;
            $result =~ s/[_\W]+/_/g;
            $result =~ s/_on$//i; # I VCI display on -> I VCI display
            $result =~ s/(\d+)[[:punct:]](\d+)/${1}${2}/g;
            $result =~ s/(dc)_(\d+)/${1}${2}/i; # VDD18C_dc_1 -> VDD18C_dc1
        }
        if ($fields{pin_char} eq '+') { # test group
            $result = 'T' . $fields{test_no} . '_' . $result;
        } elsif ($pin_name ne '-') {
            $result = $pin_name . '_' . lc $result;
            $result = 'I_' . $result if $fields{unit} =~ /^[mun]?A$/i;
        }
        $fields{item_name} = $result;
    }
    $self->update_data_names();
    return $self->check_unique();
}

sub enter_names {
    my $self = shift;
    my $log_struct = $self->{Data}{Log_Struct};
    tie my %fhash, 'FieldHash', $log_struct->[0];
    for my $i (1 .. $#{$log_struct}) {
        (tied %fhash)->data($log_struct->[$i]);
        print STDERR join("\t", @{$log_struct->[0]}), "\n";
        print STDERR join("\t", @{$log_struct->[$i]}), "\n";
        print STDERR "Enter new name: ($fhash{item_name})";
        chomp(my $enter = <STDIN>);
        $fhash{item_name} = $enter;
    }
    1;
}

sub compare_log_structure {
    my $self = shift;
    my ($struct_new) = @_;
    my $struct_old = $self->{Data}{Log_Struct};
    my @key_items = qw(line_no test_no pin_char value_index pin_name);
    # compare the meta names, which are stored as an array, as the first element in the structure
    # if the names mismatch, this error is huge, which means a design change on the stored meta info
    for my $i (0 .. $#{ $struct_old->[0] }) {
        error("given structure names mismatch: #$i: new=[$struct_new->[0][$i]], old=[$struct_old->[0][$i]]")
            unless ($struct_new->[0][$i] eq $struct_old->[0][$i]);
    }
    # okay, make the pseudo-hash
    tie my %old_fields, 'FieldHash', $struct_old->[0];
    tie my %new_fields, 'FieldHash', $struct_new->[0];
    # individual line comparison
    for my $i (1 .. $#$struct_old) {
        (tied %old_fields)->data($struct_old->[$i]);
        (tied %new_fields)->data($struct_new->[$i]);
        for my $item (@key_items) {
            error("log structure item '$item' mismatch: old=[$old_fields{$item}], new=[$new_fields{$item}]")
                unless $old_fields{$item} eq $new_fields{$item};
        }
    }
    return 0; # no mismatch
}

sub read_names {
    my $self = shift;
    my ($filename, $separator) = @_;
    $separator = $separator || ','; # by default, field separator is ','
    ## read input
    open STRUCT_IN, '<', $filename or error("cannot open input: '$filename': $!");
    my @lines;
    while (<STRUCT_IN>) {
        next if /^(?:[\#\;]|\/\/)/ || /^\s*$/;  ## comment/empty lines acceptable
        chomp;
        s/[\r\n]//g;
        push @lines, [ split($separator, $_) ]; ## CSV format
    }
    close STRUCT_IN or warning("problem when closing '$filename': $!");
    ## compare
    error("log structures not identical!") if ($self->compare_log_structure(\@lines));
    # no mismatched item, so update
    message("updating the log structure to: ", \@lines);
    $self->{Data}{Log_Struct} = \@lines;
    # update the Dies_Data heading field names
    $self->update_data_names();
    # rename other duplicates to ensure uniqueness
    return $self->rename_duplicates();
}

sub rename_duplicates {
    my $self = shift;
    my $log_struct = $self->{Data}{Log_Struct};
    tie my %fhash, 'FieldHash', $log_struct->[0];
    my %names_tbl;
    my $ren_count = 0;
    for my $i (1 .. $#$log_struct) {
        (tied %fhash)->data($log_struct->[$i]); # put the data for inspection
        my $old_name = $fhash{item_name};
        my $new_name;
        if (defined $names_tbl{$old_name}) {
            $new_name = "${old_name}_" . ($names_tbl{$old_name} + 1);
            message("measure item $i: renamed from '$old_name' to '$new_name'");
            $fhash{item_name} = $new_name;
            $ren_count++;
        }
        $names_tbl{ $old_name } += 1;
    }
    # update the data names, if rename occured
    if ($ren_count > 0) {
        $self->update_data_names();
        message("$ren_count items renamed");
    } else {
        message("good, no rename applied");
    }
    return $ren_count;
}

sub check_unique {
    my $self = shift;
    my $log_struct = $self->{Data}{Log_Struct};
    tie my %fhash, 'FieldHash', $log_struct->[0];
    my %meas_names;
    for my $i (1 .. $#$log_struct) {
        (tied %fhash)->data($log_struct->[$i]);
        $meas_names{ $fhash{item_name} } += 1;
    }
    my $instances = 0;
    for (sort keys %meas_names) {
        if ($meas_names{$_} > 1) {
            warning("ATTENTION: duplicated name: $_: $meas_names{$_}");
            $instances += 1;
        }
    }
    message("good, there is no duplicated name") unless $instances;
    return $instances;
}

sub update_data_names {
    my $self = shift;
    my $wafer = $self->{Data};
    my $log_struct = $wafer->{Log_Struct};
    my $data_names = $wafer->{Dies_Data}[0];
    my $num_meas_items = $#{$log_struct};
    my $num_all_items = @{ $data_names };
    my $num_meta_items = $num_all_items - $num_meas_items;
    tie my %fields, 'FieldHash', $log_struct->[0];
    my @new_names = map {;
        (tied %fields)->data($log_struct->[$_]);
        $fields{item_name};
    } 1 .. $num_meas_items;
    error("oops, # of new names NOT EQUAL TO # of measured items?") unless @new_names == $num_meas_items;
    splice( @{$data_names}, $num_meta_items, $num_meas_items, @new_names );
    1;
}

1;
